/*
 * JSF Toolkit Component Framework
 * Copyright (C) 2007 Noah Sloan <iamnoah A-T gmail D0T com>
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 */
package com.jsftoolkit.gen.info;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.faces.component.UIComponent;
import javax.faces.component.UIComponentBase;
import javax.faces.event.FacesListener;
import javax.faces.render.Renderer;
import javax.faces.webapp.UIComponentELTag;

import com.jsftoolkit.utils.Utils;

/**
 * Describes a {@link UIComponent}, its {@link UIComponentELTag}, and
 * possibly its {@link Renderer}. It also contains the necessary information to
 * update faces-config.xml, taglib.xml files and .tld files.
 * <p>
 * Provides sensible defaults. Attempts to set most properties to null will be
 * ignored.
 * 
 * @author noah
 */
public class ComponentInfo extends ClassInfo {

	private Class<? extends UIComponent> superClass = UIComponentBase.class;

	private Map<String, PropertyInfo> properties = new HashMap<String, PropertyInfo>();

	private Set<ConstantInfo> constants = new HashSet<ConstantInfo>();

	private Set<Class<? extends FacesListener>> listeners = new HashSet<Class<? extends FacesListener>>();

	private String rendererType;

	private String family;

	private String type;

	private RendererInfo renderer;

	private TagInfo tag;

	private ConfigInfo config;

	/**
	 * Convenience constructor.
	 * 
	 * @param _package
	 * @param className
	 * @param family
	 * @param type
	 * @param rendererType
	 */
	public ComponentInfo(String _package, String className, String family,
			String type, String rendererType) {
		this();
		this.setPackage(_package);
		this.setClassName(className);
		this.rendererType = rendererType;
		this.family = family;
		this.type = type;
	}

	/**
	 * Default constructor. Initializes config, renderer, and tag with new
	 * instances using the {@link ChildInfo#ChildInfo(ClassInfo)} constructor.
	 * 
	 */
	public ComponentInfo() {
		super();
		this.config = new ConfigInfo(this);
		this.renderer = new RendererInfo(this);
		this.tag = new TagInfo(this);
	}

	/**
	 * 
	 * @return the set of extra constants this component should declare. Should
	 *         rarely be needed.
	 */
	public Set<ConstantInfo> getConstants() {
		return constants;
	}

	/**
	 * 
	 * @return the properties of the component, in a map for convenient lookup
	 *         by constant name.
	 */
	public Map<String, PropertyInfo> getProperties() {
		return properties;
	}

	/**
	 * 
	 * @return the super class of this component
	 */
	public Class<? extends UIComponent> getSuperClass() {
		return superClass;
	}

	/**
	 * 
	 * @return the family of the component
	 */
	public String getFamily() {
		return family;
	}

	/**
	 * 
	 * @return the default renderer type of the component
	 */
	public String getRendererType() {
		return rendererType;
	}

	/**
	 * 
	 * @return the component type
	 */
	public String getType() {
		return type;
	}

	public void setType(String type) {
		if (type != null) {
			this.type = type;
		}
	}

	public void setFamily(String componentFamily) {
		if (componentFamily != null) {
			this.family = componentFamily;
		}
	}

	public void setRendererType(String defaultRendererType) {
		if (defaultRendererType != null) {
			this.rendererType = defaultRendererType;
		}
	}

	public void setConstants(Set<ConstantInfo> constants) {
		if (constants != null) {
			this.constants = constants;
		}
	}

	public void setProperties(Map<String, PropertyInfo> properties) {
		if (properties != null) {
			this.properties = properties;
		}
	}

	public void setSuperClass(Class<? extends UIComponent> superClass) {
		if (superClass != null) {
			this.superClass = superClass;
		}
	}

	/**
	 * 
	 * @return information about the configuration files to be updated for this
	 *         component
	 */
	public ConfigInfo getConfig() {
		return config;
	}

	public void setConfig(ConfigInfo config) {
		this.config = config;
	}

	/**
	 * 
	 * @return information about the component's renderer
	 */
	public RendererInfo getRenderer() {
		return renderer;
	}

	public void setRenderer(RendererInfo renderer) {
		this.renderer = renderer;
	}

	/**
	 * 
	 * @return information about the component's tag handler
	 */
	public TagInfo getTag() {
		return tag;
	}

	public void setTag(TagInfo tag) {
		this.tag = tag;
	}

	public Set<Class<? extends FacesListener>> getListeners() {
		return listeners;
	}

	public void setListeners(Set<Class<? extends FacesListener>> listeners) {
		this.listeners = listeners;
	}

	/**
	 * Adds a constant.
	 * 
	 * @param o
	 */
	public void addConstant(ConstantInfo o) {
		constants.add(o);
	}

	/**
	 * Adds a property. The property name will be converted to a constant name
	 * via {@link Utils#toConstantName(CharSequence)} and used as the map key.
	 * 
	 * @param info
	 */
	public void addProperty(PropertyInfo info) {
		properties.put(Utils.toConstantName(info.getName()), info);
	}

	/**
	 * Adds a type of listener that can be attached to this component.
	 * 
	 * @param o
	 */
	public void addListener(Class<? extends FacesListener> o) {
		listeners.add(o);
	}

	@Override
	public String toString() {
		return getCanonicalClassName() + " - " + getType() + " in "
				+ getFamily();
	}

}
